﻿'Description:有限产能排产基类
'Copyright (c) : 通力凯顿（北京）系统集成有限公司
'Writer:Wangjh 
'create Date:20220511
'Rewriter:
'Rewrite Date:
Imports DatabaseAccess.DBUtility
Imports BusinessRule.DTO
Imports System.Text
Imports External.APICalling
Imports BusinessRule.Common
Imports Newtonsoft.Json

Public MustInherit Class LimitedScheduleBaseBusiness

#Region "属性"

#End Region

#Region "正常方法，无sql"

    ''' <summary>
    ''' 判定工序维护的加工时间是否正确
    ''' </summary>
    ''' <param name="stepDt"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function ValidateStepCycle(ByVal stepDt As DataTable) As Boolean
        '验证工序时间格式
        Dim temp = stepDt.AsEnumerable().Where(Function(item)
                                                   Dim _temp = 0
                                                   If Not String.IsNullOrWhiteSpace(item("shipcycle").ToString()) Then
                                                       If Not Single.TryParse(item("shipcycle").ToString(), _temp) Then
                                                           Return True
                                                       End If
                                                   End If

                                                   If Not String.IsNullOrWhiteSpace(item("commicycle").ToString()) Then
                                                       If Not Single.TryParse(item("commicycle").ToString(), _temp) Then
                                                           Return True
                                                       End If
                                                   End If

                                                   If Not String.IsNullOrWhiteSpace(item("transfercycle").ToString()) Then
                                                       If Not Single.TryParse(item("transfercycle").ToString(), _temp) Then '转运周期不为整数
                                                           Return True
                                                       End If
                                                   End If

                                                   If Not String.IsNullOrWhiteSpace(item("makecycle").ToString()) Then
                                                       If Not Single.TryParse(item("makecycle").ToString(), _temp) Then '加工周期不为整数或小数
                                                           Return True
                                                       End If
                                                   End If

                                                   If Not String.IsNullOrWhiteSpace(item("preparecycle").ToString()) Then
                                                       If Not Single.TryParse(item("preparecycle").ToString(), _temp) Then '准备周期不为整数或小数
                                                           Return True
                                                       End If
                                                   End If

                                                   If Not String.IsNullOrWhiteSpace(item("chargingqty").ToString()) Then
                                                       If Not Integer.TryParse(item("chargingqty").ToString(), _temp) Then '装槽量必须整数
                                                           Return True
                                                       End If
                                                   End If

                                                   Return False
                                               End Function)

        If temp.Count > 0 Then
            Return False
        Else
            Return True
        End If

    End Function

    ''' <summary>
    ''' 日期偏移量，用于假期时自动偏移
    ''' </summary>
    ''' <param name="type">正序:1或倒序:2</param>
    ''' <param name="d">日期</param>
    ''' <param name="makeCalendarDt">日历</param>
    ''' <param name="add">日期再次叠加量</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function GetDateOffset(ByVal type As Integer, ByVal d As Date, ByVal makeCalendarDt As DataTable, ByVal add As Integer) As Integer
        Dim offset = 0 '日期偏移量
        If type = 1 Then '正序

            While makeCalendarDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = d.AddDays(offset + add).Date).AsDataView.Count > 0
                offset += 1
            End While

            'While makeCalendarDt.Select(String.Format("Holidaydate='{0}'", d.AddDays(offset + add).Date)).Length > 0
            '    offset += 1
            'End While
            Return offset

        ElseIf type = 2 Then '倒序
            While makeCalendarDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = d.AddDays(-(offset + add)).Date).AsDataView.Count > 0
                offset += 1
            End While

            'While makeCalendarDt.Select(String.Format("Holidaydate='{0}'", d.AddDays(-(offset + add)).Date)).Length > 0
            '    offset += 1
            'End While
            Return -offset
        Else
            Return 0
        End If

    End Function


    ''' <summary>
    ''' 根据传入时间和班次，获取真正开始或结束时间
    ''' eg:2022-5-20 08:30:00
    ''' </summary>
    ''' <param name="d"></param>
    ''' <param name="workshift">格式例子：08:30:00</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function GetRunStartOrEndDate(ByVal d As Date, ByVal workshift As String) As Date
        Dim result = d.Date
        result = result.AddHours(Val(workshift.Split(":")(0)))
        result = result.AddMinutes(Val(workshift.Split(":")(1)))
        result = result.AddSeconds(Val(workshift.Split(":")(2)))

        Return result
    End Function


    ''' <summary>
    ''' 获取两个时间相差的分钟总数，向上取整
    ''' </summary>
    ''' <param name="endDate"></param>
    ''' <param name="startDate"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetDifferMins(ByVal endDate As Date, ByVal startDate As Date) As Single
        Dim re = (endDate - startDate).TotalSeconds
        Return Single.Parse(Math.Ceiling(Decimal.Parse(re) / 60))
    End Function

    ''' <summary>
    ''' 当时间在班次的最早开始和最晚结束时间内时，可用此方法
    ''' 正排的开始，倒排的结束可用
    ''' </summary>
    ''' <param name="type">1正排 2倒排</param>
    ''' <param name="d"></param>
    ''' <param name="stepShiftDt">班次详细列表</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function GetResApsStartOrEndDate(ByVal type As Integer, ByVal d As Date, ByVal stepShiftDt As DataTable, ByVal dayShiftDt As DataTable) As Date
        If stepShiftDt.Rows.Count = 0 Then
            Return d
        End If

        Dim _stepShiftDt = stepShiftDt.Copy

        Dim resStart = GetRunStartOrEndDate(d, _stepShiftDt.Rows(0)("startdate").ToString) '设备最早开始时间
        Dim resEnd = GetRunStartOrEndDate(d, _stepShiftDt.Rows(0)("enddate").ToString) '设备最晚结束时间

        '------查询新增班次日历的数据，更新班次最早最晚时间 add by zhangrj 20250923------------
        For Each drd As DataRow In dayShiftDt.Rows
            Dim rStart = GetRunStartOrEndDate(d, drd("starttime")) '开始时间
            Dim rEnd = GetRunStartOrEndDate(d, drd("endtime")) '结束时间
            If resStart > rStart Then '如果设备初始开始时间大于该班次的开始时间，则更新设备初始开始时间
                resStart = rStart
            End If
            If resEnd < rEnd Then '如果设备初始结束时间小于该班次的结束时间，则更新设备初始结束时间
                resEnd = rEnd
            End If
            '将新的班次存入班次列表中去
            Dim drNew As DataRow = _stepShiftDt.NewRow()
            drNew("startdatedetail") = drd("starttime").ToString()
            drNew("enddatedetail") = drd("endtime").ToString()
            _stepShiftDt.Rows.Add(drNew)
        Next
        '-------------------------------------

        For Each stepShiftRow In _stepShiftDt.Rows
            Dim startdatedetail = GetRunStartOrEndDate(d, stepShiftRow("startdatedetail").ToString)
            Dim enddatedetail = GetRunStartOrEndDate(d, stepShiftRow("enddatedetail").ToString)

            If Val(stepShiftRow("enddatedetail").ToString.Split(":")(0)) < Val(stepShiftRow("startdatedetail").ToString.Split(":")(0)) Then '跨天
                enddatedetail = enddatedetail.AddDays(1)
            End If

            If d >= startdatedetail And d <= enddatedetail Then '在有效加工时间内
                Return d
            End If

            stepShiftRow("startdatedetail") = startdatedetail.ToString("yyyy/MM/dd HH:mm:ss")
            stepShiftRow("enddatedetail") = enddatedetail.ToString("yyyy/MM/dd HH:mm:ss")

        Next
        _stepShiftDt.AcceptChanges()

        If type = 1 Then '正排
            '到此说明不在有效加工时间内
            If d < resStart Then
                d = resStart
            ElseIf d > resEnd Then
                d = resStart.AddDays(1)
            Else
                If _stepShiftDt.Rows.Count >= 2 Then
                    For i As Integer = 1 To _stepShiftDt.Rows.Count - 1
                        If d > Date.Parse(_stepShiftDt.Rows(i - 1)("enddatedetail")) And d < Date.Parse(_stepShiftDt.Rows(i)("startdatedetail")) Then '判断在哪个休息时间段
                            d = Date.Parse(_stepShiftDt.Rows(i)("startdatedetail"))
                        End If
                    Next
                End If
            End If

        ElseIf type = 2 Then '倒排
            '到此说明不在有效加工时间内
            If d < resStart Then
                d = resEnd.AddDays(-1)
            ElseIf d > resEnd Then
                d = resEnd
            Else
                If _stepShiftDt.Rows.Count >= 2 Then
                    For i As Integer = 1 To _stepShiftDt.Rows.Count - 1
                        If d > Date.Parse(_stepShiftDt.Rows(i - 1)("enddatedetail")) And d < Date.Parse(_stepShiftDt.Rows(i)("startdatedetail")) Then '判断在哪个休息时间段
                            d = Date.Parse(_stepShiftDt.Rows(i - 1)("enddatedetail"))
                        End If
                    Next
                End If
            End If
        Else
            Return d
        End If

        Return d

    End Function

    ''' <summary>
    ''' 当时间需要累加分钟数时，可用此方法 eg:班次时间段为 8:30-12:00,13:00-16:00,16:30-17:30三个时间段,考虑了班次跨天的情况
    ''' 正排的结束时间，倒排的开始时间可用
    ''' </summary>
    ''' <param name="type"></param>
    ''' <param name="d"></param>
    ''' <param name="addMinites">追加分钟数</param>
    ''' <param name="stepShiftDt">班次</param>
    ''' <param name="stepResMKDt">日历</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function GetResApsEndOrStartDate(ByVal type As Integer, ByVal d As Date, ByVal addMinites As Single, ByVal stepShiftDt As DataTable, ByVal stepResMKDt As DataTable, ByVal resourceId As String) As Date
        Dim orgD = d
        If stepShiftDt.Rows.Count = 0 Then
            If type = 1 Then
                d = d.AddMinutes(addMinites)
            ElseIf type = 2 Then
                d = d.AddMinutes(-addMinites)
            End If
        End If

        If stepShiftDt.Rows.Count > 0 Then
            '------------更新班次最早最晚时间 add by zhangrj 20250923----------------
            Dim dtDayWorkshift = GetDayWorkShiftListByResourceID(resourceId, d) '获取设备在这一天的新的班次明细
            For Each drd As DataRow In dtDayWorkshift.Rows
                Dim rStart = GetRunStartOrEndDate(d, drd("starttime")) '开始时间
                Dim rEnd = GetRunStartOrEndDate(d, drd("endtime")) '结束时间
                If DateTime.Parse(stepShiftDt.Rows(0)("startdate").ToString) > rStart Then '如果设备初始开始时间大于该班次的开始时间，则更新设备初始开始时间
                    stepShiftDt.Rows(0)("startdate") = rStart
                End If
                If DateTime.Parse(stepShiftDt.Rows(0)("enddate").ToString) < rEnd Then '如果设备初始结束时间小于该班次的结束时间，则更新设备初始结束时间
                    stepShiftDt.Rows(0)("enddate") = rEnd
                End If
                '将新的班次存入班次列表中去
                Dim drNew As DataRow = stepShiftDt.NewRow()
                drNew("startdatedetail") = drd("starttime").ToString()
                drNew("enddatedetail") = drd("endtime").ToString()
                stepShiftDt.Rows.Add(drNew)
            Next
            stepShiftDt.AcceptChanges()
            '----------------------------
            Dim _stepShiftDt = stepShiftDt.Copy
            Dim resStart = GetRunStartOrEndDate(d, _stepShiftDt.Rows(0)("startdate").ToString) '设备最早开始时间
            Dim resEnd = GetRunStartOrEndDate(d, _stepShiftDt.Rows(0)("enddate").ToString) '设备最晚结束时间

            Dim recursionStartTime = Date.Now

            If type = 1 Then '正排
                If resEnd < resStart Then
                    resEnd = resEnd.AddDays(1)
                End If
                While (addMinites > 0)
                    If (Date.Now - recursionStartTime).TotalMinutes > 5 Then '超过5分钟的，判定为死循环，记录日志
                        addMinites = 0
                        LogHelp.WriteLog("排产出现死循环,LimitedScheduleBaseBusiness.GetResApsEndOrStartDate各参数为：" +
                                 "type：" + type.ToString +
                                 "d：" + d.ToString("yyyy-MM-dd HH:mm:ss") +
                                 "addMinites：" + addMinites.ToString +
                                 "stepShiftDt：" + JsonConvert.SerializeObject(stepShiftDt) +
                                 "stepResMKDt：" + JsonConvert.SerializeObject(stepResMKDt)
                                 )
                    End If

                    For i As Integer = 0 To _stepShiftDt.Rows.Count - 1
                        Dim stepShiftRow = _stepShiftDt.Rows(i)

                        Dim startdatedetail = GetRunStartOrEndDate(d, stepShiftRow("startdatedetail").ToString)
                        Dim enddatedetail = GetRunStartOrEndDate(d, stepShiftRow("enddatedetail").ToString)

                        If Val(stepShiftRow("enddatedetail").ToString.Split(":")(0)) < Val(stepShiftRow("startdatedetail").ToString.Split(":")(0)) Then '跨天
                            enddatedetail = enddatedetail.AddDays(1)
                        End If

                        Dim _differ = 0
                        If (d >= startdatedetail And d <= enddatedetail) Or
                            (d > GetRunStartOrEndDate(d.AddDays(-1), stepShiftRow("startdatedetail").ToString) And d < GetRunStartOrEndDate(d, stepShiftRow("enddatedetail").ToString)) Then '在此有效加工时间内
                            _differ = GetDifferMins(enddatedetail, d)
                            If addMinites > _differ Then
                                If i < _stepShiftDt.Rows.Count - 1 Then '下一个时间段的开始时间
                                    d = GetRunStartOrEndDate(d, stepShiftDt.Rows(i + 1)("startdatedetail").ToString)
                                Else '此时循环到最后一个时间段，第二天的最早开始时间
                                    d = GetRunStartOrEndDate(d, _stepShiftDt.Rows(0)("startdate").ToString).AddDays(1)
                                End If
                                addMinites = addMinites - _differ
                            Else
                                d = d.AddMinutes(addMinites)
                                addMinites = 0
                                Exit For
                            End If
                        Else '当时间d不在有效时间内,可能小于最早开始，可能大于最晚结束。此时特殊处理，防止无限循环
                            Dim isChange = False
                            If d > resEnd Then
                                d = resStart.AddDays(1)
                                isChange = True
                            End If
                            If d < resStart Then
                                d = resStart
                                isChange = True
                            End If
                            If isChange Then
                                '重新赋值
                                resStart = GetRunStartOrEndDate(d, _stepShiftDt.Rows(0)("startdate").ToString) '设备最早开始时间
                                resEnd = GetRunStartOrEndDate(d, _stepShiftDt.Rows(0)("enddate").ToString) '设备最晚结束时间
                                If resEnd < resStart Then
                                    resEnd = resEnd.AddDays(1)
                                End If
                                Exit For
                            End If
                        End If

                        If i < _stepShiftDt.Rows.Count - 1 Then
                            If d > enddatedetail And d < GetRunStartOrEndDate(d, stepShiftDt.Rows(i + 1)("startdatedetail").ToString) Then '异常，防止无限循环
                                addMinites = 0
                            End If
                        End If

                    Next
                End While
                '考虑日历
                If stepResMKDt.Rows.Count > 0 Then
                    Dim offset = 0 '偏移量
                    For i As Integer = 1 To (d.Date - orgD.Date).TotalDays
                        offset = GetDateOffset(1, orgD.AddDays(i), stepResMKDt, 0)
                        d = d.AddDays(offset)
                    Next
                End If
            ElseIf type = 2 Then '倒排
                If resEnd < resStart Then
                    resStart = resStart.AddDays(-1)
                End If
                While (addMinites > 0)
                    If (Date.Now - recursionStartTime).TotalMinutes > 5 Then '超过5分钟的，判定为死循环，记录日志
                        addMinites = 0
                        LogHelp.WriteLog("排产出现死循环,LimitedScheduleBaseBusiness.GetResApsEndOrStartDate各参数为：" +
                                 "type：" + type.ToString +
                                 "d：" + d.ToString("yyyy-MM-dd HH:mm:ss") +
                                 "addMinites：" + addMinites.ToString +
                                 "stepShiftDt：" + JsonConvert.SerializeObject(stepShiftDt) +
                                 "stepResMKDt：" + JsonConvert.SerializeObject(stepResMKDt)
                                 )
                    End If

                    For i As Integer = _stepShiftDt.Rows.Count - 1 To 0 Step -1
                        Dim stepShiftRow = _stepShiftDt.Rows(i)

                        Dim startdatedetail = GetRunStartOrEndDate(d, stepShiftRow("startdatedetail").ToString)
                        Dim enddatedetail = GetRunStartOrEndDate(d, stepShiftRow("enddatedetail").ToString)

                        If Val(stepShiftRow("enddatedetail").ToString.Split(":")(0)) < Val(stepShiftRow("startdatedetail").ToString.Split(":")(0)) Then '跨天
                            startdatedetail = startdatedetail.AddDays(-1)
                        End If

                        Dim _differ = 0
                        If (d >= startdatedetail And d <= enddatedetail) Or
                            (d > GetRunStartOrEndDate(d, stepShiftRow("startdatedetail").ToString) And d < GetRunStartOrEndDate(d.AddDays(1), stepShiftRow("enddatedetail").ToString)) Then '在此有效加工时间内
                            _differ = GetDifferMins(d, startdatedetail)
                            If addMinites > _differ Then

                                If i > 0 Then '下一个时间段的开始时间
                                    d = GetRunStartOrEndDate(d, stepShiftDt.Rows(i - 1)("enddatedetail").ToString)
                                Else '此时循环到第一个时间段，前一天的最后结束时间
                                    d = GetRunStartOrEndDate(d, _stepShiftDt.Rows(0)("enddate").ToString).AddDays(-1)
                                End If
                                addMinites = addMinites - _differ
                            Else
                                d = d.AddMinutes(-addMinites)
                                addMinites = 0
                                Exit For
                            End If
                        Else '当时间d不在有效时间内,可能小于最早开始，可能大于最晚结束。此时特殊处理，防止无限循环
                            Dim isChange = False
                            If d > resEnd Then
                                d = resEnd
                                isChange = True
                            End If
                            If d < resStart Then
                                d = resEnd.AddDays(-1)
                                isChange = True
                            End If
                            If isChange Then
                                '重新赋值
                                resStart = GetRunStartOrEndDate(d, _stepShiftDt.Rows(0)("startdate").ToString) '设备最早开始时间
                                resEnd = GetRunStartOrEndDate(d, _stepShiftDt.Rows(0)("enddate").ToString) '设备最晚结束时间
                                If resEnd < resStart Then
                                    resStart = resStart.AddDays(-1)
                                End If
                                Exit For
                            End If
                        End If

                        If i > 0 Then
                            If d < startdatedetail And d > GetRunStartOrEndDate(d, stepShiftDt.Rows(i - 1)("enddatedetail").ToString) Then '异常，防止无限循环
                                addMinites = 0
                            End If
                        End If
                    Next
                End While
                '考虑日历
                If stepResMKDt.Rows.Count > 0 Then
                    Dim offset = 0 '偏移量
                    For i As Integer = 1 To (orgD.Date - d.Date).TotalDays
                        offset = GetDateOffset(2, orgD.AddDays(-i), stepResMKDt, 0)
                        d = d.AddDays(offset)
                    Next
                End If
            End If
        End If

        Return d
    End Function

    ''' <summary>
    ''' 计算实际占用的分钟数
    ''' </summary>
    ''' <param name="occupyStart">占用开始时间</param>
    ''' <param name="occupyEnd">占用结束时间</param>
    ''' <param name="resShiftStart">班次最早开始时间</param>
    ''' <param name="resShiftEnd">班次最晚结束时间</param>
    ''' <param name="stepShiftDt">班次</param>
    ''' <param name="stepResMKDt">日历</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function GetActualOccupyMins2(ByVal occupyStart As Date, ByVal occupyEnd As Date, ByVal resShiftStart As String, ByVal resShiftEnd As String,
                                 ByVal stepShiftDt As DataTable, ByVal stepResMKDt As DataTable) As Single
        Dim _occupyStart = occupyStart, _occupyEnd = occupyEnd
        Dim re = 0
        '计算实际占用分钟数，考虑班次，休息时间
        Dim _days = (occupyEnd.Date - occupyStart.Date).TotalDays
        For j As Integer = 0 To _days
            _occupyStart = occupyStart.Date.AddDays(j)
            _occupyEnd = occupyStart.Date.AddDays(j + 1).AddMinutes(-1)

            '如果此占用的日期正在放假，不计算
            If stepResMKDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = _occupyStart.Date).AsDataView.Count > 0 Then
                Continue For
            End If

            If j = 0 Then
                _occupyStart = occupyStart
            End If
            If j = _days Then
                _occupyEnd = occupyEnd
            End If

            re += GetActualOccupyMins(_occupyStart, _occupyEnd, resShiftStart, resShiftEnd, stepShiftDt)

        Next
        Return re
    End Function

    ''' <summary>
    ''' 计算实际占用的分钟数,占用开始和结束在同天的情况
    ''' </summary>
    ''' <param name="occupyStart">占用开始时间</param>
    ''' <param name="occupyEnd">占用结束时间</param>
    ''' <param name="resShiftStart">班次最早开始时间</param>
    ''' <param name="resShiftEnd">班次最晚结束时间</param>
    ''' <param name="stepShiftDt">班次</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function GetActualOccupyMins(ByVal occupyStart As Date, ByVal occupyEnd As Date, ByVal resShiftStart As String, ByVal resShiftEnd As String,
                                 ByVal stepShiftDt As DataTable) As Single
        If stepShiftDt.Rows.Count = 0 Then
            Return GetDifferMins(occupyEnd, occupyStart)
        End If
        Dim occupyMins As Single = 0 '实际占用的分钟数
        Dim occupyRestMins As Single = 0 '占用的休息时间
        Dim resStart = GetRunStartOrEndDate(occupyStart, resShiftStart) '设备最早开始时间
        Dim resEnd = GetRunStartOrEndDate(occupyStart, resShiftEnd) '设备最晚结束时间
        If resEnd <= resStart Then
            resEnd = resEnd.AddDays(1)
        End If

        Dim _stepShiftDt = stepShiftDt.Copy()
        For i As Integer = 0 To _stepShiftDt.Rows.Count - 1
            Dim stepShiftRow = _stepShiftDt.Rows(i)
            Dim startdatedetail = GetRunStartOrEndDate(occupyStart, stepShiftRow("startdatedetail").ToString)
            Dim enddatedetail = GetRunStartOrEndDate(occupyStart, stepShiftRow("enddatedetail").ToString)

            If Val(stepShiftRow("enddatedetail").ToString.Split(":")(0)) < Val(stepShiftRow("startdatedetail").ToString.Split(":")(0)) Then '跨天
                enddatedetail = enddatedetail.AddDays(1)
            End If
            stepShiftRow("startdatedetail") = startdatedetail.ToString("yyyy/MM/dd HH:mm:ss")
            stepShiftRow("enddatedetail") = enddatedetail.ToString("yyyy/MM/dd HH:mm:ss")
        Next
        _stepShiftDt.AcceptChanges()
        '注释说明{：设备最早开始时间，}：设备最晚结束时间。[：占用开始时间,]:占用结束时间

        If Not (resEnd < occupyStart And occupyEnd < resStart) Then '此种情况下产生实际占用
            If occupyEnd > resEnd Then '[````{```````````}````] 或者 '{``````[````}`````] 情况下才会占用有效时间段
                For i As Integer = _stepShiftDt.Rows.Count - 1 To 0 Step -1
                    Dim stepShiftRow = _stepShiftDt.Rows(i)

                    If occupyStart >= Date.Parse(stepShiftRow("startdatedetail").ToString) And occupyStart <= Date.Parse(stepShiftRow("enddatedetail").ToString) Then
                        occupyMins += GetDifferMins(Date.Parse(stepShiftRow("enddatedetail").ToString), occupyStart)
                    Else
                        If occupyStart < Date.Parse(stepShiftRow("startdatedetail").ToString) Then
                            occupyMins += GetDifferMins(Date.Parse(stepShiftRow("enddatedetail").ToString), Date.Parse(stepShiftRow("startdatedetail").ToString))
                        End If
                    End If
                Next
            Else '[````{```````````]````} 或者 '{``````[```````]``} 情况
                '取有效结束时间
                Dim occupyEndTemp = occupyEnd
                For i As Integer = _stepShiftDt.Rows.Count - 1 To 0 Step -1
                    Dim stepShiftRow = _stepShiftDt.Rows(i)

                    If occupyEndTemp >= Date.Parse(stepShiftRow("startdatedetail").ToString) And occupyEndTemp <= Date.Parse(stepShiftRow("enddatedetail").ToString) Then
                        Exit For
                    Else
                        If i > 0 Then
                            If occupyEndTemp >= Date.Parse(_stepShiftDt.Rows(i - 1)("enddatedetail").ToString) Then
                                occupyEndTemp = Date.Parse(_stepShiftDt.Rows(i - 1)("enddatedetail").ToString)
                            End If
                        End If
                    End If
                Next
                '计算有效占用时间
                For i As Integer = _stepShiftDt.Rows.Count - 1 To 0 Step -1
                    Dim stepShiftRow = _stepShiftDt.Rows(i)

                    If Not (occupyEndTemp >= Date.Parse(stepShiftRow("startdatedetail").ToString) And occupyEndTemp <= Date.Parse(stepShiftRow("enddatedetail").ToString)) Then
                        Continue For
                    End If

                    If occupyStart >= Date.Parse(stepShiftRow("startdatedetail").ToString) And occupyStart <= Date.Parse(stepShiftRow("enddatedetail").ToString) Then
                        occupyMins += GetDifferMins(occupyEndTemp, occupyStart)
                    Else
                        If occupyStart < Date.Parse(stepShiftRow("startdatedetail").ToString) Then
                            occupyMins += GetDifferMins(occupyEndTemp, Date.Parse(stepShiftRow("startdatedetail").ToString))
                        End If
                        If i > 0 Then
                            occupyEndTemp = Date.Parse(_stepShiftDt.Rows(i - 1)("enddatedetail").ToString)
                        End If
                    End If
                Next
            End If

        End If


        Return occupyMins
    End Function

    ''' <summary>
    ''' 计算工序的加工周期和设备的开机周期
    ''' add:Wangjh 20221130
    ''' </summary>
    ''' <param name="conBaseRow">当前批次信息</param>
    ''' <param name="conDt">所有批次信息</param>
    ''' <param name="stepRow">当前工序信息</param>    
    ''' <param name="resourceTable">设备列表</param>
    ''' <remarks></remarks>
    Protected Sub GetStepCycle(ByVal conBaseRow As DataRow, ByVal conDt As DataTable, ByVal stepRow As DataRow, ByRef resourceTable As DataTable)
        Dim stepCycle As Single = 0 '工序加工周期
        Dim stepCyCle2 As Single = 0 '设备开机结束周期 
        Dim containerQty As Integer = Integer.Parse(conBaseRow("qty").ToString) '批次数量
        Dim isMachineParallel As String = stepRow("multimachineparallel").ToString '是否多设备并行加工
        'Dim perQty As Integer = 0 '分配后的数量
        If Not resourceTable.Columns.Contains("stepcycle") Then
            resourceTable.Columns.Add("stepcycle")
        End If
        If Not resourceTable.Columns.Contains("stepcycle2") Then
            resourceTable.Columns.Add("stepcycle2")
        End If

        If stepRow("isship").ToString = "1" Then '转工序
            '转运时间由天改为小时 yuxj
            'stepCycle = Val(stepRow("shipcycle").ToString) * 24 * 60
            stepCycle = Single.Parse((stepRow("shipcycle").ToString)) * 60
            stepCyCle2 = stepCycle
        ElseIf stepRow("controlcode").ToString = "LY05" Then '外委序
            stepCycle = Single.Parse((stepRow("commicycle").ToString)) * 24 * 60
            stepCyCle2 = stepCycle
        Else '本车间加工序，yuxj20230309增加工序多设备并行逻辑
            Dim imod As Integer = containerQty Mod resourceTable.Rows.Count
            Dim iavg As Integer = containerQty / resourceTable.Rows.Count
            If isMachineParallel = "0" Then
                iavg = containerQty
            End If
            For i As Integer = 0 To resourceTable.Rows.Count - 1
                '将多出来的余数放在最后一台
                If isMachineParallel = "1" And i = resourceTable.Rows.Count - 1 Then
                    iavg = containerQty - (iavg * i)
                End If

                stepCycle = Single.Parse(stepRow("transfercycle").ToString) + iavg * Single.Parse(stepRow("makecycle").ToString) +
                    Single.Parse(stepRow("preparecycle").ToString)
                stepCyCle2 = iavg * Single.Parse(stepRow("makecycle").ToString) + Single.Parse(stepRow("preparecycle").ToString)

                If stepRow("attributevalue").ToString() = "602" Then '会不会改名或多个包含热表字段的数据？
                    ''yuxj热表增加装炉量槽量计算
                    If Integer.Parse(stepRow("chargingqty").ToString) > 0 Then
                        Dim chargingTimes As Integer = Math.Ceiling(iavg / Integer.Parse(stepRow("chargingqty").ToString))
                        '批次30 装炉量15(--绑定在工序上, 根据工序加工数量, 如果一炉不够加工需要装多炉(一炉加工多次)) 总时间 = 准备时间 + 30/15 * 设备加工时间 + 检验时间(转运时间))
                        stepCycle = Single.Parse(stepRow("transfercycle").ToString) + chargingTimes * Single.Parse(stepRow("makecycle").ToString) +
                                                 Single.Parse(stepRow("preparecycle").ToString)

                        stepCyCle2 = chargingTimes * Single.Parse(stepRow("makecycle").ToString) + Single.Parse(stepRow("preparecycle").ToString)
                    End If 
                End If

                '多机并行后批次总加工周期的计算？
                resourceTable.Rows(i)("stepcycle") = stepCycle
                resourceTable.Rows(i)("stepcycle2") = stepCyCle2
                resourceTable.Rows(i)("qty") = iavg
            Next
            '不考虑后面并行且单件单卡的异常情况
        End If

        '注意单件单卡时间特殊处理，若选择5件，则每张单卡的加工时间为5*(准备+加工+转运)或者 准备+5*加工+转运 add;Wangjh 20221130 
        If conBaseRow("containertype").ToString = "7" Then
            Dim djdk = conDt.Select(String.Format("containertype=7 and togetherflag='{0}'", conBaseRow("togetherflag").ToString))
            If djdk.Length > 0 Then
                If djdk(0)("DJDKSPECIAL") = "1" Then 'djdk.Length*(准备+加工+转运),djdk.Length*外委周期,djdk.Length*转工周期
                    stepCycle = stepCycle * djdk.Length
                    stepCyCle2 = stepCyCle2 * djdk.Length
                ElseIf djdk(0)("DJDKSPECIAL") = "2" Then '准备+djdk.Length*加工+转运

                    If stepRow("isship").ToString = "1" Then '转工序

                    ElseIf stepRow("controlcode").ToString = "LY05" Then '外委序

                    Else '本车间
                        stepCycle = stepCycle + (djdk.Length - 1) * Single.Parse(stepRow("makecycle").ToString)
                    End If
                End If
            End If
        End If


        '转工，外委，单件单卡不考虑并行yuxj
        If isMachineParallel = "0" Or (stepRow("isship").ToString = "1" Or stepRow("controlcode").ToString = "LY05") Then
            For Each dr In resourceTable.Rows
                dr("stepcycle") = stepCycle
                dr("stepcycle2") = stepCyCle2
                dr("qty") = containerQty
            Next
        End If


    End Sub

    ''' <summary>
    ''' 计算批次排产的真正开始时间 正排
    ''' add:Wangjh 20230317
    ''' </summary>
    ''' <param name="startTime">开始日期</param>
    ''' <param name="conRow">批次排产信息</param> 
    ''' <param name="conBaseRow">批次基本信息</param> 
    ''' <param name="apsPoolDt">排产池基本信息</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function GetConApsStartTime(ByVal startTime As Date, ByVal conRow As DataRow, ByVal conBaseRow As DataRow, ByVal apsPoolDt As DataTable) As Date
        If apsPoolDt.Rows.Count = 0 Then
            Return startTime
        End If

        Dim apsTimeRule As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format("select * from ApsPoolTimeRule where ApsPoolID='{0}' order by Sequence", apsPoolDt.Rows(0)("apsPoolId").ToString))

        Dim nowDate As Date = Date.Parse(Date.Now.ToString("yyyy-MM-dd") + " 23:59:59") '记录当前日期

        For i As Integer = 0 To apsTimeRule.Rows.Count - 1
            If apsTimeRule.Rows(i)("TimeRule").ToString = "1" Then '人为指定
                If Not conBaseRow.IsNull("ExpectMaterialDate") Then '有人为指定的日期
                    If Date.Parse(conBaseRow("ExpectMaterialDate").ToString) < nowDate Then '如果该日期过期，则继续下一个规则
                        Continue For
                    End If
                    Return Date.Parse(conBaseRow("ExpectMaterialDate").ToString).AddHours(Val(apsTimeRule.Rows(i)("AddTime").ToString))
                Else '实际没有人为指定
                    Continue For
                End If
            End If

            If apsTimeRule.Rows(i)("TimeRule").ToString = "2" Then '系统当前时间基础上叠加
                Return Date.Now.AddHours(Val(apsTimeRule.Rows(i)("AddTime").ToString))
            End If

            If apsTimeRule.Rows(i)("TimeRule").ToString = "3" Then '在订单开始基础上增加时间
                If Date.Parse(conBaseRow("mfgstarttime").ToString) < nowDate Then '如果该日期过期，则继续下一个规则
                    Continue For
                End If
                Return Date.Parse(conBaseRow("mfgstarttime").ToString).AddHours(Val(apsTimeRule.Rows(i)("AddTime").ToString))
            End If

            If apsTimeRule.Rows(i)("TimeRule").ToString = "4" Then '当前工艺最后一个派工结束时间上叠加
                Dim conDispatchDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(<string>select csd.* from containerstepdispatch csd
                left join workflowstep ws on ws.workflowstepid=csd.workflowstepid
                where csd.containerid='{0}' and ws.workflowid='{1}' and csd.isuse=0
                order by csd.plannedfinishdate desc</string>.Value, conRow("containerid").ToString, conRow("workflowid").ToString))
                If conDispatchDt.Rows.Count > 0 Then
                    If Date.Parse(conDispatchDt.Rows(0)("plannedfinishdate").ToString) < nowDate Then '如果该日期过期，则继续下一个规则
                        Continue For
                    End If
                    Return Date.Parse(conDispatchDt.Rows(0)("plannedfinishdate").ToString).AddHours(Val(apsTimeRule.Rows(i)("AddTime").ToString))
                End If
            End If
        Next
        Return startTime

    End Function

    ''' <summary>
    ''' 判定一个班次是否包含另一个班次,targetStart是否包含在org中
    ''' add:Wangjh 20230328
    ''' </summary>
    ''' <param name="orgStart"></param>
    ''' <param name="orgEnd"></param>
    ''' <param name="targetStart"></param>
    ''' <param name="targetEnd"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function WorkShiftIncludeOtherShift(ByVal orgStart As String, ByVal orgEnd As String, ByVal targetStart As String, ByVal targetEnd As String) As Boolean
        If String.IsNullOrWhiteSpace(orgStart) Or String.IsNullOrWhiteSpace(orgEnd) Then '为空认为24小时加班
            Return True
        End If

        If String.IsNullOrWhiteSpace(targetStart) Or String.IsNullOrWhiteSpace(targetEnd) Then '为空认为是24小时上班
            Return False
        End If

        Dim d As Date = Date.Now
        Dim orgStartDate = GetRunStartOrEndDate(d, orgStart)
        Dim orgEndDate = GetRunStartOrEndDate(d, orgEnd)

        Dim targetStartDate = GetRunStartOrEndDate(d, targetStart)
        Dim targetEndDate = GetRunStartOrEndDate(d, targetEnd)

        If Val(orgEnd.Split(":")(0)) < Val(orgStart.Split(":")(0)) Then '班次跨天
            orgEndDate = orgEndDate.AddDays(1)
            targetEndDate = targetEndDate.AddDays(1)
        End If
        If orgStartDate <= targetStartDate And orgEndDate >= targetEndDate Then
            Return True
        Else
            Return False
        End If

    End Function

    ''' <summary>
    ''' 判定设备的开始结束时间，是否满足人员排班情况
    ''' </summary>
    ''' <param name="empWorkDateDt">人员排班情况</param> 
    ''' <param name="stepResMKDt">设备日历</param>
    ''' <param name="stepShiftDt">设备班次</param>
    ''' <param name="simulateStart">设备模拟开始时间</param>
    ''' <param name="simulateEnd">设备模拟结束时间</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function ResDateMatchEmpWorkDate(ByVal empWorkDateDt As DataTable, ByVal stepResMKDt As DataTable, ByVal stepShiftDt As DataTable,
                                        ByVal simulateStart As Date, ByVal simulateEnd As Date) As Boolean

        Dim resShiftStart = "00:00:00"  '设备班次最早
        Dim resShiftEnd = "23:59:00"  '设备班次最晚
        If stepShiftDt.Rows.Count > 0 Then
            resShiftStart = stepShiftDt.Rows(0)("startdate").ToString()
            resShiftEnd = stepShiftDt.Rows(0)("enddate").ToString
        End If

        '先将设备开始结束日期分割为一天天模式
        Dim simulateDate As New DataTable
        simulateDate.Columns.Add("simulateStart")
        simulateDate.Columns.Add("simulateEnd")
        Dim diffDays = (simulateEnd - simulateStart).Days
        For i As Integer = 0 To diffDays
            Dim _simulateStart = simulateStart.AddDays(i)
            If stepResMKDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = _simulateStart.Date).Count > 0 Then '休假
                Continue For
            End If
            If i > 0 Then
                _simulateStart = GetRunStartOrEndDate(_simulateStart, resShiftStart)
            End If
            Dim _simulateEnd = GetRunStartOrEndDate(_simulateStart, resShiftEnd)

            If i = diffDays Then
                _simulateEnd = simulateEnd
            End If

            If _simulateEnd < _simulateStart Then '班次跨天
                _simulateEnd = _simulateEnd.AddDays(1)
            End If
            simulateDate.Rows.Add(_simulateStart.ToString("yyyy-MM-dd HH:mm:ss"), _simulateEnd.ToString("yyyy-MM-dd HH:mm:ss"))
        Next
        simulateDate.AcceptChanges()

        For Each dRow As DataRow In simulateDate.Rows

            Dim _simulateStart = Date.Parse(dRow("simulateStart").ToString)
            Dim _simulateEnd = Date.Parse(dRow("simulateEnd").ToString)

            If empWorkDateDt.AsEnumerable().Where(Function(r)
                                                      Dim empStart = Date.Parse(r("workdate").ToString + " " + r("startdate").ToString) '当天人员工作开始时间
                                                      Dim empEnd = Date.Parse(r("workdate").ToString + " " + r("enddate").ToString) '当天人员工作结束时间
                                                      If empEnd < empStart Then
                                                          empEnd = empEnd.AddDays(1)
                                                      End If
                                                      If empStart <= _simulateStart And empEnd >= _simulateEnd Then '合适
                                                          Return True
                                                      End If
                                                      Return False
                                                  End Function).Count = 0 Then '排班不满足设备模拟加工的时间

                Return False

            End If
        Next

        Return True
    End Function
#End Region

#Region "正常方法，有sql"

    ''' <summary>
    ''' 排产工序二次筛选
    ''' add:Wangjh 20230316
    ''' </summary>
    ''' <param name="stepDt">需要筛选的工序信息</param>
    ''' <param name="stepApsDt">工序排产信息</param>
    ''' <param name="conRow">批次信息</param> 
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function FilterApsStep(ByVal stepDt As DataTable, ByVal stepApsDt As DataTable, ByVal conRow As DataRow) As DataTable
        Dim result As DataTable = stepDt.Clone

        Dim processingSql = "select * from containersteprecord csr where csr.containerID='{0}'"

        Dim processingDt = DatabaseHelper.Query_ReturnDatTable(String.Format(processingSql, conRow("containerID").ToString))

        '不加工信息
        Dim noProcessingDt = DatabaseHelper.Query_ReturnDatTable(String.Format(<string>select cp.* from containerstepnoship cp where cp.containerid='{0}' and cp.noship=2 and cp.isuse=0</string>.Value, conRow("containerID").ToString))
        '外委信息
        Dim commisDt = DatabaseHelper.Query_ReturnDatTable(String.Format(<string>select cl.workflowstepid,cm.indate from containercommissioning cm
left join containercommidetail cl on cl.containercommissioningid=cm.containercommissioningid and cl.isuse=0
where cm.containerid='{0}' and cl.workflowid='{1}' and cm.isuse=0 </string>.Value, conRow("containerID").ToString, conRow("workflowid").ToString))

        For i As Integer = 0 To stepDt.Rows.Count - 1
            '当前序已下发或已确认或已接收或已接收撤销，不参与排产
            If stepApsDt.Select("workflowstepid='" + stepDt.Rows(i)("workflowstepid").ToString + "' and (Status='1' or status='2' or status='3' or status='4' )").Length > 0 Then
                result.Rows.Clear() '之前所有序不参与排产
                Continue For
            End If
            '当前版本工序有加工，不参与排产。
            If processingDt.Select("workflowstepid='" + stepDt.Rows(i)("workflowstepid").ToString + "'").Length > 0 Then
                If stepDt.Rows(i)("lastcheck").ToString = "1" Then '如果是检验序，必须要有检验记录后才不参与排产 modify:wangjh 20240812
                    If Not String.IsNullOrWhiteSpace(processingDt.Select("workflowstepid='" + stepDt.Rows(i)("workflowstepid").ToString + "'")(0)("checkdate").ToString) Then
                        result.Rows.Clear() '之前所有序不参与排产
                        Continue For
                    End If
                Else
                    result.Rows.Clear() '之前所有序不参与排产
                    Continue For
                End If
            End If
            '其他版本工序已完工，不参与排产
            If processingDt.Select("workflowstepname='" + stepDt.Rows(i)("workflowstepname").ToString + "' and usecompleted=1").Length > 0 Then
                result.Rows.Clear() '之前所有序不参与排产
                Continue For
            End If
            '已维护工序不加工，不参与排产
            If noProcessingDt.Select("workflowstepid='" + stepDt.Rows(i)("workflowstepid").ToString + "'").Length > 0 Then
                Continue For
            End If
            '已有转入的外委，不参与排产
            If commisDt.Select("workflowstepid='" + stepDt.Rows(i)("workflowstepid").ToString + "' AND indate IS NOT NULL").Length > 0 Then
                result.Rows.Clear() '之前所有序不参与排产
                Continue For
            ElseIf commisDt.Select("workflowstepid='" + stepDt.Rows(i)("workflowstepid").ToString + "' AND indate IS  NULL").Length > 0 Then '有手动外委的算外委序
                stepDt.Rows(i)("CONTROLCODE") = "LY05"
            End If

            result.ImportRow(stepDt.Rows(i))

        Next
        result.AcceptChanges()

        Return result
    End Function

    ''' <summary>
    ''' 获取排产池基本信息
    ''' </summary>
    ''' <param name="id"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function Aps_GetApsPoolDt(ByVal id As String) As DataTable
        Dim sql = <string>select al.* from apspool al where al.apspoolid='{0}'</string>.Value

        sql = String.Format(sql, id)
        Return DatabaseHelper.Query_ReturnDatTable(sql)
    End Function

    ''' <summary>
    ''' 获取工序基本信息，用于aps计算
    ''' </summary>
    ''' <param name="workflowId"></param>
    ''' <param name="beginSeq"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function Aps_GetStepBaseDt(ByVal workflowId As String, Optional ByVal beginSeq As Integer = 0, Optional ByVal endSeq As Integer = 0, Optional ByVal isbottleneck As Integer = 0) As DataTable
        Dim sql = <string>SELECT ws.sequence,decode(nvl(ws.shipcycle,0),0,168,ws.shipcycle) shipcycle,nvl(ws.transfercycle,0) transfercycle,nvl(ws.makecycle,0) makecycle,
nvl(ws.preparecycle,0) preparecycle,nvl(ws.commicycle,0) commicycle,ws.workflowstepname,ws.workflowstepid,s.specid,ws.workflowid,
decode(s.factoryid,w.factoryid,0,1) isship,s.factoryid specfactoryid,nvl(ws.controlcode,s.controlcode) controlcode,NVL(s.lastcheck,0) lastcheck,
nvl(ws.MakeEmpCounts,0) MakeEmpCounts,ws.workcenterid,ws.teamid,nvl(ws.chargingqty,0) chargingqty,u.attributevalue,nvl(ws.multimachineparallel,0) multimachineparallel
  from workflow w
left join workflowstep ws on ws.workflowid=w.workflowid
left join specbase sb on sb.specbaseid=ws.specbaseid
left join spec s on s.specid=nvl(sb.revofrcdid,ws.specid)
left join userattribute u on u.parentid=w.factoryid and u.userattributename='分厂代码'
where w.workflowid='{0}' and ws.Sequence>{1} </string>.Value
        If endSeq <> 0 Then
            sql += String.Format(" and ws.Sequence<{0} ", endSeq)
        End If
        If isbottleneck <> 0 Then
            sql += <string> and exists (select 1 from specresourceinfo sc 
 left join resourcedef rd on rd.resourceid=sc.resourceid where sc.specid=s.specid and rd.isbottleneck=1)</string>.Value
        End If
        sql += "order by ws.workflowstepid"
        sql = String.Format(sql, workflowId, beginSeq)
        Return DatabaseHelper.Query_ReturnDatTable(sql)
    End Function

    ''' <summary>
    ''' 获取批次基本信息，用于aps计算
    ''' </summary>
    ''' <param name="conId"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function Aps_GetConBaseDt(ByVal conId As String) As DataTable
        Dim sql = <string>select cci.apspoolid,cci.containerid,cci.containername,cci.qty,fa.makecalendarid,cci.togetherflag,cci.containertype,m.plannedstartdate mfgstarttime,
to_char(cci.ExpectMaterialDate,'yyyy-mm-dd hh24:mi:ss') ExpectMaterialDate from containercurrentinfo cci
left join factory fa on fa.factoryid=cci.originalfactoryid 
left join mfgorder m on m.mfgorderid=cci.mfgorderid 
where cci.containerid='{0}'</string>.Value
        sql = String.Format(sql, conId)
        Return DatabaseHelper.Query_ReturnDatTable(sql)
    End Function

    ''' <summary>
    ''' 获取日历基本信息
    ''' </summary>
    ''' <param name="id"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function Aps_GetMakeCalendarDt(ByVal id As String) As DataTable
        Dim sql = <string>select ml.Holidaydate from MakeCalendarDetail ml where ml.MakeCalendarID='{0}' and ml.isuse=0</string>.Value
        sql = String.Format(sql, id)
        Return DatabaseHelper.Query_ReturnDatTable(sql)
    End Function

    ''' <summary>
    ''' 获取工序的设备信息
    ''' </summary>
    ''' <param name="specIds"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function Aps_GetSpecResourceDt(ByVal specIds As String) As DataTable
        Dim strSql = <string>select decode(rd.resourcename,null,'',rd.description||'('||rd.resourcename||')') RESNAME,sc.specid,rd.resourceid,rd.resourcename,rd.description,rd.makecalendarid,rd.workshiftid,rd.prioritynum,rd.isbottleneck from specresourceinfo sc 
                    left join resourcedef rd on rd.resourceid=sc.resourceid --and rd.factoryid=s.factoryid 
                    where sc.specid in ({0}) and nvl(rd.isuse,0)=0 order by rd.prioritynum desc</string>.Value
        strSql = String.Format(strSql, specIds)
        Return DatabaseHelper.Query_ReturnDatTable(strSql)
    End Function

    ''' <summary>
    ''' 获取设备日历信息
    ''' </summary>
    ''' <param name="resIds"></param> 
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function Aps_GetResCalendarDt(ByVal resIds As String) As DataTable
        Dim strSql = <string>select rd.resourceid,md.Holidaydate from resourcedef rd
left join MakeCalendar mc on mc.makecalendarid=rd.makecalendarid and mc.isuse=0
left join MakeCalendarDetail md on md.makecalendarid=mc.makecalendarid and md.isuse=0
where rd.resourceid in ({0}) and nvl2(md.makecalendarid,'1','0')='1' 
union
select rd.resourceid,md.Holidaydate from resourcedef rd
left join MakeCalendar mc on mc.makecalendarid=rd.nextmakecalendarid and mc.isuse=0
left join MakeCalendarDetail md on md.makecalendarid=mc.makecalendarid and md.isuse=0
where rd.resourceid in ({0}) and nvl2(md.makecalendarid,'1','0')='1' 
</string>.Value
        strSql = String.Format(strSql, resIds)
        Return DatabaseHelper.Query_ReturnDatTable(strSql)
    End Function

    ''' <summary>
    ''' 获取设备班次信息
    ''' </summary>
    ''' <param name="resIds"></param> 
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function Aps_GetResWorkShiftDt(ByVal resIds As String) As DataTable
        Dim strSql = <string>select distinct rd.resourceid,wf.workshiftid,wf.startdate,wf.enddate,wd.startdate startdatedetail,wd.enddate enddatedetail,wd.mins,wd.sequence from workshift wf
left join workshiftdetail wd on wd.workshiftid=wf.workshiftid
left join resourcedef rd on rd.workshiftid=wf.workshiftid
where rd.resourceid in ({0}) and wf.isuse=0
order by wd.sequence </string>.Value
        strSql = String.Format(strSql, resIds)
        Return DatabaseHelper.Query_ReturnDatTable(strSql)
    End Function

    ''' <summary>
    ''' 获取设备人员信息
    ''' </summary>
    ''' <param name="resIds"></param> 
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function Aps_GetResEmpDt(ByVal resIds As String) As DataTable
        Dim strSql = <string>select rd.resourcename, rd.resourceid,e.employeeid,e.employeename,e.fullname,e.skillnum,e.makecalendarid,e.workshiftid,e.IsSchedule from resourcedef rd
left join employeeresourcelist el on el.resourcelistid=rd.resourceid
left join employee e on e.employeeid=el.employeeid
where rd.resourceid in ({0}) order by e.skillnum desc </string>.Value
        strSql = String.Format(strSql, resIds)
        Return DatabaseHelper.Query_ReturnDatTable(strSql)
    End Function


    ''' <summary>
    ''' 获取设备保养信息,指定时间之后
    ''' </summary>
    ''' <param name="resIds"></param>
    ''' <param name="strStartTime"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function Aps_GetResUpKeepDtAfterTime(ByVal resIds As String, ByVal strStartTime As String) As DataTable
        Dim strSql = "select rk.* from ResourceUpKeepInfo rk where rk.resourceid in ({0}) and rk.upkeepstartdate>=to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
" or (" +
" rk.upkeepstartdate<to_date('{1}','yyyy-mm-dd hh24:mi:ss') and rk.upkeependdate>to_date('{1}','yyyy-mm-dd hh24:mi:ss')" +
")"
        strSql = String.Format(strSql, resIds, strStartTime)
        Return DatabaseHelper.Query_ReturnDatTable(strSql)
    End Function

    ''' <summary>
    ''' 获取设备保养信息,指定时间之前
    ''' </summary>
    ''' <param name="resIds"></param>
    ''' <param name="strEndTime"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function Aps_GetResUpKeepDtBeforeTime(ByVal resIds As String, ByVal strEndTime As String) As DataTable
        Dim strSql = "select rk.* from ResourceUpKeepInfo rk where rk.resourceid in ({0}) and rk.upkeependdate<=to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
" or (" +
" rk.upkeepstartdate<to_date('{1}','yyyy-mm-dd hh24:mi:ss') and rk.upkeependdate>to_date('{1}','yyyy-mm-dd hh24:mi:ss')" +
")"
        strSql = String.Format(strSql, resIds, strEndTime)
        Return DatabaseHelper.Query_ReturnDatTable(strSql)
    End Function
    ''' <summary>
    ''' 根据批次ID/工序ID, 获取预排工序齐套数据 add by longb 20250813
    ''' </summary>
    ''' <param name="containerId">批次ID</param>
    ''' <param name="workflowstepId">工序ID</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function Aps_GetApsPlaninfoPreSpotCheck(ByVal containerId As String, ByVal workflowstepId As String) As DataTable
        Dim strSql = <string>
                    select ap.workflowstepname,a.estallsetdate,a.allset from apsplaninfoprespotcheck a 
                    left join apsplandetailpre ap on ap.apsplandetailid =a.apsplandetailpreid
                    where ap.containerid ='{0}' and ap.workflowstepid ='{1}' 
                     </string>.Value
        strSql = String.Format(strSql, containerId, workflowstepId)
        Return DatabaseHelper.Query_ReturnDatTable(strSql)
    End Function

    ''' <summary>
    ''' 通过设备ID获取某一天的班次
    ''' </summary>
    ''' <param name="resourceID"></param>
    ''' <param name="dayDate"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function GetDayWorkShiftListByResourceID(ByVal resourceID As String, ByVal dayDate As Date) As DataTable
        Dim sql = String.Format(<string>
                      SELECT rs.starttime,rs.endtime FROM resourceworkshiftdayInfo rs
                      WHERE rs.resourceid = '{0}' AND rs.daydate = to_date('{1}','yyyy-mm-dd')
                  </string>.Value, resourceID, dayDate.ToShortDateString())
        Return DatabaseHelper.Query_ReturnDatTable(sql)
    End Function
#End Region

#Region "抽象"
    ''' <summary>
    ''' 设备占用信息sql
    ''' 当前结束时间之前,查询这些设备被排产后占用的情况，结果集需要排除已完工的。
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected MustOverride Property Sql_ConApsSelect As String
    Protected MustOverride Property Sql_ConApsUpdate As String
    Protected MustOverride Property Sql_ConApsInsert As String

    Protected MustOverride Property Sql_StepApsSelect As String
    Protected MustOverride Property Sql_StepApsInsert As String
    Protected MustOverride Property Sql_StepApsUpdate As String
    Protected MustOverride Property Sql_StepApsDel As String

    Protected MustOverride Property Sql_StepApsRange As String

    ''' <summary>
    ''' 正排
    ''' </summary>
    ''' <param name="conDt"></param>
    ''' <param name="para"></param>
    ''' <param name="IsBottleneck"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    MustOverride Function LMPositiveCalculate(ByVal conDt As DataTable, ByVal para As Dictionary(Of String, String), Optional ByVal IsBottleneck As Boolean = False,
                                              Optional ByVal ApsOccupyConIdList As List(Of String) = Nothing, Optional ByVal apsTempTable As String = "") As uMESProcessResult

    ''' <summary>
    ''' 倒排
    ''' </summary>
    ''' <param name="conDt"></param>
    ''' <param name="para"></param>
    ''' <param name="IsBottleneck"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    MustOverride Function LMReverseCalculate(ByVal conDt As DataTable, ByVal para As Dictionary(Of String, String), Optional ByVal IsBottleneck As Boolean = False) As uMESProcessResult

    ''' <summary>
    ''' 瓶颈排
    ''' </summary>
    ''' <param name="conDt"></param>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    MustOverride Function LMBottleneckCalculate(ByVal conDt As DataTable, ByVal para As Dictionary(Of String, String)) As uMESProcessResult

    ''' <summary>
    ''' 排产调整
    ''' </summary>
    ''' <param name="conDt"></param>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    MustOverride Function LMAPSChange(ByVal conDt As DataTable, ByVal para As Dictionary(Of String, String)) As uMESProcessResult

    ''' <summary>
    ''' 获取设备占用信息，指定时间之后
    ''' </summary>
    ''' <param name="resIds"></param>
    ''' <param name="conIdList"></param>
    ''' <param name="startTime"></param>
    ''' <param name="apsType"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected MustOverride Function GetResOccupyDtAfterTime(ByVal resIds As String, ByVal conIdList As List(Of String), ByVal startTime As String, ByVal apsType As String, Optional apsTempTable As String = "") As DataTable

    ''' <summary>
    ''' 获取设备占用信息，指定时间之前
    ''' </summary>
    ''' <param name="resIds"></param>
    ''' <param name="conIdList"></param>
    ''' <param name="endTime"></param>
    ''' <param name="apsType"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected MustOverride Function GetResOccupyDtBeforeTime(ByVal resIds As String, ByVal conIdList As List(Of String), ByVal endTime As String, ByVal apsType As String) As DataTable

    ''' <summary>
    ''' 主数据查询
    ''' </summary>
    ''' <param name="para"></param>
    ''' <param name="isPage"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    MustOverride Function GetApsPoolContainerInfo(ByVal para As Dictionary(Of String, String), Optional ByVal isPage As Boolean = False) As uMESProcessResult

    ''' <summary>
    ''' 删除排产数据
    ''' </summary>
    ''' <param name="conApsID">排产ID</param>
    ''' <param name="seq">工序顺序</param>
    ''' <remarks></remarks>
    MustOverride Sub DeleteApsPlanData(ByVal conApsID As String, ByVal seq As Integer, Optional ByVal status As String = "")

    ''' <summary>
    ''' 删除排产数据
    ''' </summary>
    ''' <param name="containerId"></param>
    ''' <param name="stepIds"></param>
    ''' <param name="type">1正排，2倒排，3瓶颈排产</param>
    ''' <remarks></remarks>
    MustOverride Sub DeleteApsPlanData(ByVal containerId As String, ByVal stepIds As String, Optional ByVal type As String = "1")

    ''' <summary>
    ''' 删除排产数据
    ''' </summary>
    ''' <param name="apsplandetailIds"></param>
    ''' <remarks></remarks>
    MustOverride Sub DeleteApsPlanData(ByVal apsplandetailIds As List(Of String))

    ''' <summary>
    ''' 获取子卡对应主卡工序的开始结束时间
    ''' </summary>
    ''' <param name="sonConDt"></param>
    ''' <param name="apsType"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    MustOverride Function GetSonContainerStartEndDate(ByVal sonConDt As DataTable, ByVal apsType As String) As DataTable
#End Region

End Class
